package com.Multithreading.Synchronization;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import static java.lang.System.getLogger;
import static java.lang.System.out;

public class ConditionalLockableBank extends LockableBank{
    /*
    * 有些情况下，我们需要先对数据进行检测，符合某些条件时才进行下一步操作，但如果使用简单的if else语句进行判定
    * 线程仍能被打断，因此存在检查通过后若该进程被打断，另一线程修改了该数据使得修改后的数据无法满足条件而产生错误
    * 如果检查前就利用对象锁，则将陷入忙等状态，其他满足条件的线程也无法获得锁。
    *
    * 因此需要用到条件变量
    * */

    private final Condition fund_condition;
    public ConditionalLockableBank(int n, double initialBalance) {
        super(n, initialBalance);
        // 为对象锁创建条件变量
        this.fund_condition = this.bankLock.newCondition();
    }

    @Override
    public void transform(int from, int to, double money) {
        // 1.获得锁
        bankLock.lock();
        try {
            if(this.accounts[from] < money) {
                // 2.当条件不允许时该线程将加入该条件的阻塞集合中
                this.fund_condition.await();
            }
            out.println(Thread.currentThread());
            this.accounts[from] -= money;
            out.println("This transfer: " + from + " to " + to + " amount: " + money);
            this.accounts[to] += money;
            out.println(this.getTotalBalance());
            // 任何存在条件变量的线层都应该在退出区后唤醒该条件阻塞集合中的线程，即使集合中没有线程
            /*
                条件变量拥有signal 和 signalAll方法，区别在于signal随机唤醒一个被阻塞的线程，
                而被唤醒的线程可能仍不满足条件无法执行，由于没有继续唤醒其他线程，系统将会进入死锁状态。
                而signalAll方法将会唤醒所有阻塞集合中的线程，只要有一个线程满足运行条件，系统将不会进入死锁。
                因此使用signalAll更安全，因为要唤醒所有线程检查条件，开销也会更大。
            */
            this.fund_condition.signalAll();
        }catch (InterruptedException e) {
            getLogger(e.toString());
        } finally {
            bankLock.unlock();
        }
    }
}
